//
// Created by denglibin on 19-10-24.
//
/**
 * 信号量： 控制对有限个资源的访问 （如果资源数是1就是互斥量）
 *信号量
信号量(Semaphore)，有时被称为信号灯，是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源。

信号量可以分为几类：

² 二进制信号量(binary semaphore)：只允许信号量取0或1值，其同时只能被一个线程获取。

² 整型信号量（integer semaphore)：信号量取值是整数，它可以被多个线程同时获得，直到信号量的值变为0。

² 记录型信号量（record semaphore)：每个信号量s除一个整数值value（计数）外，还有一个等待队列List，其中是阻塞在该信号量的各个线程的标识。
 当信号量被释 放一个，值被加一后，系统自动从等待队列中唤醒一个等待中的线程，让其获得信号量，同时信号量再减一。

信号量通过一个计数器控制对共享资源的访问，信号量的值是一个非负整数，所有通过它的线程都会将该整数减一。如果计数器大于0，则访问被允许，计数器减1；
 如果为0，则访问被禁止，所有试图通过它的线程都将处于等待状态。

 计数器计算的结果是允许访问共享资源的通行证。因此，为了访问共享资源，线程必须从信号量得到通行证， 如果该信号量的计数大于0，则此线程获得一个通行证，
 这将导致信号量的计数递减，否则，此线程将阻塞直到获得一个通行证为止。当此线程不再需要访问共享资 源时，它释放该通行证，这导致信号量的计数递增，
 如果另一个线程等待通行证，则那个线程将在那时获得通行证。

 sem_init:
该函数用于创建信号量，其原型如下：
int sem_init(sem_t *sem, int pshared, unsigned int value);
该函数初始化由 sem 指向的信号对象，并给它一个初始的整数值 value。
pshared 控制信号量的类型，值为 0 代表该信号量用于多线程间的同步，值如果大于 0 表示可以共享，用于多个相关进程间的同步


 int sem_destroy(sem_t *sem);,其中sem是要销毁的信号量。只有用sem_init初始化的信号量才能用sem_destroy销毁。

 int sem_wait(sem_t *sem);
 等待信号量，如果信号量的值大于0,将信号量的值减1,立即返回。如果信号量的值为0,则线程阻塞。
 相当于P操作。成功返回0,失败返回-1。

 int sem_post(sem_t *sem); 释放信号量，让信号量的值加1。相当于V操作


 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>

/*使用信号量实现生产者消费者模型*/

static unsigned int resource_num = 0; // 资源池资源数
static unsigned int resource_blank_num = 5; // 资源池剩余空间大小
static sem_t pro_blank_num, con_resource_num; // 声明两个信号量 用于生产者的空间数，用于消费者的资源数
/**
 * 生产者
 * @param arg
 * @return
 */
static void* t_pro(void * arg){

    while (1){
        sem_wait(&pro_blank_num); // 等待信号量，如果信号量的值大于0,将信号量的值减1（空间减少）,立即返回。如果信号量的值为0,则线程阻塞
        printf("pro\n");
        sem_post(&con_resource_num); //释放信号量，让信号量的值加1（生产了一个资源） 同时唤醒在该信号量上阻塞的线程
        sleep(1);
    }
}
/**
 * 消费者
 * @param arg
 * @return
 */
static void* t_con(void * arg){
    while (1){
        sem_wait(&con_resource_num); // 资源信号量为0时，表示没资源 阻塞，不为0则资源数减1 返回
        printf("con\n");
        sem_post(&pro_blank_num); // 空间加+1，通知生产者生产
        sleep(1);
    }
}

static int main_(void){
    // 初始化信号量
    sem_init(&pro_blank_num, 0, resource_blank_num);
    sem_init(&con_resource_num, 0, resource_num);

    pthread_t pro_t1;
    pthread_t pro_t2;
    pthread_t pro_t3;
    pthread_t con_t1;
    pthread_t con_t2;
    pthread_t con_t3;


    //生产者
    pthread_create(&pro_t1, NULL, t_pro, NULL);
    pthread_create(&pro_t2, NULL, t_pro, NULL);
    pthread_create(&pro_t3, NULL, t_pro, NULL);

    //消费者
    pthread_create(&con_t1, NULL, t_con, NULL);
    pthread_create(&con_t2, NULL, t_con, NULL);
    pthread_create(&con_t3, NULL, t_con, NULL);

    pthread_join(pro_t1, NULL);
    pthread_join(pro_t2, NULL);
    pthread_join(pro_t3, NULL);
    pthread_join(con_t1, NULL);
    pthread_join(con_t2, NULL);
    pthread_join(con_t3, NULL);

    // 销毁信号量
    sem_destroy(&pro_blank_num);
    sem_destroy(&con_resource_num);

    return 0;
}


